﻿using System;
using System.Diagnostics;
using System.Threading;

namespace SuiHan.Commons.Logging {
    internal class LoggerImpl : Logger {
        private readonly string category;
        private static readonly int mainThreadId = Thread.CurrentThread.ManagedThreadId;
        private static bool IsMainThread => mainThreadId == Thread.CurrentThread.ManagedThreadId;

        public LoggerConfig Config { get; set; }

        internal LoggerImpl(string category) {
            this.category = category;
        }

        private string GetCurrentDateTime() {
            return DateTime.Now.ToString(Config.DateFormat);
        }

        private string GetMethodName() {
            StackFrame frame = new StackTrace().GetFrame(3);
            string name = frame?.GetMethod()?.Name;
            return name != null ? '.' + name : null;
        }

        private void Log(string level, string content, params object[] args) {
            string date = GetCurrentDateTime();
            string threadName = Thread.CurrentThread.Name;
            if (string.IsNullOrEmpty(threadName)) {
                threadName = IsMainThread ? "main" : "worker";
            }

            string methodName = GetMethodName();
            string format = $"{date} [{threadName}] {level} {category}{methodName} {content}";
            string log = args.Length > 0 ? string.Format(format, args) : format;
            if (Config.FileEnabled) Config.LogWriter.Write(log);
            if (Config.ConsoleEnabled) Console.WriteLine(log);
        }

        public void Trace(string content) {
            if (Config.Level <= LoggerLevel.TRACE) {
                Log("TRACE", content);
            }
        }

        public void Trace(string format, params object[] args) {
            if (Config.Level <= LoggerLevel.TRACE) {
                Log("TRACE", format, args);
            }
        }

        public void Trace(string content, Exception cause) {
            if (Config.Level <= LoggerLevel.TRACE) {
                Log("TRACE", content + cause);
            }
        }

        public void Trace(Exception cause) {
            if (Config.Level <= LoggerLevel.DEBUG) {
                Log("DEBUG", cause?.ToString());
            }
        }

        public void Debug(string content) {
            if (Config.Level <= LoggerLevel.DEBUG) {
                Log("DEBUG", content);
            }
        }

        public void Debug(string format, params object[] args) {
            if (Config.Level <= LoggerLevel.DEBUG) {
                Log("DEBUG", format, args);
            }
        }

        public void Debug(string content, Exception cause) {
            if (Config.Level <= LoggerLevel.DEBUG) {
                Log("DEBUG", content + cause);
            }
        }

        public void Debug(Exception cause) {
            if (Config.Level <= LoggerLevel.DEBUG) {
                Log("DEBUG", cause?.ToString());
            }
        }

        public void Info(string content) {
            if (Config.Level <= LoggerLevel.INFO) {
                Log("INFO", content);
            }
        }

        public void Info(string format, params object[] args) {
            if (Config.Level <= LoggerLevel.INFO) {
                Log("INFO", format, args);
            }
        }

        public void Info(string content, Exception cause) {
            if (Config.Level <= LoggerLevel.INFO) {
                Log("INFO", content + cause);
            }
        }

        public void Info(Exception cause) {
            if (Config.Level <= LoggerLevel.INFO) {
                Log("INFO", cause?.ToString());
            }
        }

        public void Warn(string content) {
            if (Config.Level <= LoggerLevel.WARN) {
                Log("WARN", content);
            }
        }

        public void Warn(string format, params object[] args) {
            if (Config.Level <= LoggerLevel.WARN) {
                Log("WARN", format, args);
            }
        }

        public void Warn(string content, Exception cause) {
            if (Config.Level <= LoggerLevel.WARN) {
                Log("WARN", content + cause);
            }
        }

        public void Warn(Exception cause) {
            if (Config.Level <= LoggerLevel.WARN) {
                Log("WARN", cause?.ToString());
            }
        }

        public void Error(string content) {
            if (Config.Level <= LoggerLevel.ERROR) {
                Log("ERROR", content);
            }
        }

        public void Error(string format, params object[] args) {
            if (Config.Level <= LoggerLevel.ERROR) {
                Log("ERROR", format, args);
            }
        }

        public void Error(string content, Exception cause) {
            if (Config.Level <= LoggerLevel.ERROR) {
                Log("ERROR", content + cause);
            }
        }

        public void Error(Exception cause) {
            if (Config.Level <= LoggerLevel.ERROR) {
                Log("ERROR", cause?.ToString());
            }
        }
    }
}
